#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sge_safe_client_interface.h"
#include "sge_api.h"
#include "sge_service.h"
#include "sge_session.h"
#include "sge_util.h"

const char *g_sge_id = "sge";
const char *g_trader_id = "demo_trader";

#define ENC_MODE_SOFT 1
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8888 
#define HEART_BEAT_SEND_INTERVAL 1
#define HEART_BEAT_TIMEOUT 20

typedef struct api_ctx_s{
    api_safe_client_t *safe_api_;
    api_safe_client_session_t *safe_sess_;
    api_safe_client_link_t *safe_link_;
    int shakehand_state_;
}api_ctx_t;

int get_svr_msg_type(const char *msg, unsigned int con_len){
    // get gtp msg type

    return 0;
}

#define HANDSHAKED 1
int __is_safe_handshake_finished(api_ctx_t ctx){
    if(ctx.shakehand_state_ == HANDSHAKED)
        return 1;
    return 0;
}

fld_server_hello_t *gtp2gems_server_hello(const char *msg, unsigned int con_len){
    // convert gtp to fld_server_hello_t

    // mock
    fld_server_hello_t *svr_hello = (fld_server_hello_t*)malloc(sizeof(fld_server_hello_t));

    return svr_hello;
}

int handle_svr_hello(api_safe_client_session_t *ptr, api_safe_client_link_t *enc_link, const char *msg, unsigned int con_len){
    // 1. gtp->gems: server hello
    fld_server_hello_t *svr_hello = gtp2gems_server_hello(msg, con_len);
    if(!svr_hello){
        printf("convert gtp to server hello failed\n");
        return -1;
    }

    // 2. handle server hello
    int ret = sge_safe_client_handle_hello_rsp(ptr, enc_link, svr_hello);
    free(svr_hello);
    if(0 != ret){
        printf("handle server hello failed! error code:%d\n", ret);
        return -2;
    }

    // 3. create authentication
    fld_authentication_info_t *auth_info = (fld_authentication_info_t *)calloc(1, sizeof(fld_authentication_info_t));
    ret = sge_safe_client_create_authentication(ptr, enc_link, auth_info);
    if(0 != ret){
        printf("create authentication info failed! error code:%d\n", ret);
        return -3;
    }

    // 4. convert auth info to gtp

    // 5. send gtp

    free(auth_info);

    return 0;
}

fld_sessionkey_info_t *gtp2gems_sessionkey(const char *msg, unsigned int con_len){
    // convert gtp to fld_sessionkey_info_t

    // mock
    fld_sessionkey_info_t *sessionkey = (fld_sessionkey_info_t*)malloc(sizeof(fld_sessionkey_info_t));

    return sessionkey;
}

int handle_svr_session_key(api_safe_client_session_t *ptr, api_safe_client_link_t *enc_link, const char *msg, unsigned int con_len){
    // 1. gtp to server sessionkey
    fld_sessionkey_info_t *sessionkey = gtp2gems_sessionkey(msg, con_len);
    if(!sessionkey){
        printf("convert gtp2gems sessionkey failed!\n");
        return -1;
    }

    // 2. accept sessionkey
    int ret = sge_safe_client_accept_sessionkey(ptr, enc_link, sessionkey);
    free(sessionkey);
    if(0 != ret){
        printf("handle session key failed! error code:%d\n", ret);
        return -2;
    }

    return 0;
}

#define MSG_SVR_HELLO 1
#define MSG_SVR_SESSION_KEY 2
static void __recv_msg(int type, const char * msg, unsigned int con_len, void * ctx) {
    api_ctx_t *api_ctx = (api_ctx_t*)ctx;
    if(!api_ctx){
        printf("invalid api ctx!\n");
        return;
    }

    int ret;
    if(MSG_TYPE_PLAIN == type){
        int msg_type = get_svr_msg_type(msg, con_len);
        switch(msg_type){
            case MSG_SVR_HELLO:
                ret = handle_svr_hello(api_ctx->safe_sess_, api_ctx->safe_link_, msg, con_len);
                if(0 != ret){
                    // disconnect
                }
                break;
            case MSG_SVR_SESSION_KEY:
                ret = handle_svr_session_key(api_ctx->safe_sess_, api_ctx->safe_link_, msg, con_len);
                if(0 != ret){
                    // disconnect
                }
                else {
                    api_ctx->shakehand_state_ = HANDSHAKED;                  
                }
                break;
            default:
                printf("error svr message!\n");
                // disconnect
        }
    }
    else if(MSG_TYPE_CIPHER == type){
        unsigned char *buf = (unsigned char*)malloc(con_len);
        unsigned int cap;
        ret = sge_safe_client_packet_decrypt(api_ctx->safe_sess_, api_ctx->safe_link_, (unsigned char*)msg, con_len, buf, &cap);
        if(0 != ret){
            printf("decrypt msg failed, error code:%d\n", ret);
            // disconnect
        }
        printf("recv msg:%s\n", buf);
        // process msg
        free(buf);
    }
    else{
        printf("invalid msg type!\n");
        // disconnect
    }
}

char *svr_enc_pubkey = "server enc pubkey";
char *svr_sign_pubkey = "server sign pubkey";

session_client_info_t *create_session_client_info(){
    session_client_info_t *session_info = (session_client_info_t*)malloc(sizeof(session_client_info_t));
    if(!session_info)
        return NULL;

    unsigned int len = sizeof(session_info->sge_id_);
    strncpy(session_info->sge_id_, g_sge_id, len);
    session_info->sge_id_[len - 1] = '\0';
    len = sizeof(session_info->trader_id_);
    strncpy(session_info->trader_id_, g_trader_id, len);
    session_info->trader_id_[len - 1] = '\0';
    session_info->enc_mode_ = ENC_MODE_SOFT;
    memcpy(session_info->svr_enc_pubkey_, svr_enc_pubkey, strlen(svr_enc_pubkey));
    session_info->svr_enc_pubkey_len_ = strlen(svr_enc_pubkey);
    memcpy(session_info->svr_sign_pubkey_, svr_sign_pubkey, strlen(svr_sign_pubkey));
    session_info->svr_sign_pubkey_len_ = strlen(svr_sign_pubkey);

    return session_info;
}

static void __cb_connected(void * ctx) {
    api_ctx_t *api_ctx = (api_ctx_t*)ctx;
    if(!api_ctx){
        printf("invalid api ctx!\n");
        // disconnect
        return;
    }

    // 1. create session info
    session_client_info_t *session_info = create_session_client_info();
    if(NULL == session_info){
        printf("alloc session client info failed!\n");
        // disconnect
        return;
    }

    // 2. create session handle
    api_ctx->safe_sess_ = sge_safe_create_client_session(api_ctx->safe_api_, session_info);
    free(session_info);
    if(!api_ctx->safe_sess_){
        printf("create session handle failed\n");
        goto err;
    }

    // 3. create client hello
    fld_client_hello_t *cli_hello = (fld_client_hello_t *)calloc(1, sizeof(fld_client_hello_t));
    int ret = sge_safe_client_create_hello(api_ctx->safe_sess_, cli_hello);
    if(ret != 0){
        printf("create client hello failed!\n");
        goto err;
    }

    // 4.convert client hello to gtp

    // 5.send gtp

    free(cli_hello);
    return;

err:
    if(session_info)
        free(session_info);
    if(api_ctx->safe_sess_){
        sge_safe_free_client_session(api_ctx->safe_sess_);
        api_ctx->safe_sess_ = NULL;
    }
}

static void __cb_disconnect(int error_no, void * ctx) {
    api_ctx_t *api_ctx = (api_ctx_t*)ctx;
    if(!api_ctx){
        printf("invalid api ctx!\n");
        return;
    }

    // free session
    if(api_ctx->safe_sess_){
        sge_safe_free_client_session(api_ctx->safe_sess_);
        api_ctx->safe_sess_ = NULL;
    }
}

void send_msg(api_ctx_t *api_ctx){
    char *login_msg = "X03=A100";
    unsigned int msg_len = strlen(login_msg);
    unsigned char *dst = (unsigned char*)calloc(1, msg_len + 16);
    int dst_cap = msg_len + 16;
    int ret = sge_safe_client_packet_encrypt(api_ctx->safe_sess_, api_ctx->safe_link_, (unsigned char*)login_msg, msg_len, dst, &dst_cap);
    if(0 != ret){
        printf("encrypt msg failed!\n");
    }

    // send msg

    free(dst);
}
void __service_on_run(void * ptr)
{
    printf("sge_service_on_run\n");
    return;
}
void __service_on_stop(void * ptr)
{
    printf("sge_service_on_stop \n");
    return;
}

#define TIMEOUT_ONE_SECOND  1
int main(int argc, char *argv[]){
    api_ctx_t api_ctx = { 0 };

    // 1. create safe api
    api_ctx.safe_api_ = sge_safe_client_api_create(NULL, ENC_MODE_SOFT);
    if(!api_ctx.safe_api_){
        printf("create safe api failed!\n");
        goto end;
    }

    // 2. create safe link
    api_ctx.safe_link_ = sge_safe_client_link_create(api_ctx.safe_api_);
    if(api_ctx.safe_link_ == NULL){
        printf("sge_safe_client_link_create failed!\n");
        goto end;
    }
        
    // 3. create link api
    sge_service_cb_t *svr_callbacks = (sge_service_cb_t *)calloc(1,sizeof(sge_service_cb_t));
    if(svr_callbacks == NULL){
        printf("calloc svr_callbacks failed!\n");
        goto end;
    }
    svr_callbacks->on_before_run_ = __service_on_run;
    svr_callbacks->on_after_stop_ = __service_on_stop;
    
    sge_service_t * link_api = sge_service_create("./log", "test", svr_callbacks, NULL);
    if(link_api == NULL){
        printf("create link api failed!\n");
        goto end;
    }
    int ret = sge_service_run(link_api);
    if(ret != 0){
        printf("sge_service_run failed!\n");
        goto end;
    }
    
    free(svr_callbacks);

    // 4. create link sess
    sge_session_callbacks_t *sess_cb = (sge_session_callbacks_t *)calloc(1,sizeof(sge_session_callbacks_t));
    if(sess_cb == NULL){
        printf("calloc sess cb error!\n");
        goto end;
    }
    sess_cb->on_read_ = __recv_msg;
    sess_cb->on_connected_ = __cb_connected;
    sess_cb->on_disconnected_ = __cb_disconnect;
    sge_session_t *link_sess = sge_session_create(link_api, SERVER_IP, SERVER_PORT, sess_cb, HEART_BEAT_SEND_INTERVAL, HEART_BEAT_TIMEOUT, &api_ctx);
    if(link_sess == NULL){
        printf("sge_session_create failed!\n");
        goto end;
    }
    free(sess_cb);

    ret = sge_session_connect(link_sess);
    if(ret != 0){
        printf("sge_session_connect failed!\n");
        goto end;
    }


    // 5. wait server session key
    while(__is_safe_handshake_finished(api_ctx) != 1) {
        ;
    }

    // 6. login, order apply, ..., logout
    send_msg(&api_ctx);

end:
    if(api_ctx.safe_link_)
        sge_safe_client_link_destroy(api_ctx.safe_link_);
    if(api_ctx.safe_api_)
        sge_safe_client_api_destroy(api_ctx.safe_api_);
    if(link_sess)
        sge_session_disconnect(link_sess);
    if(link_api){
        sge_service_stop(link_api);
        sge_service_destroy(link_api);
    }

    return 0;
}

